package test.dataprovider;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;

import test.dataprovider.resultprocessor.IResultProcessor;
import test.util.Constants;

public abstract class BaseDataProvider<K extends Serializable, V extends Serializable> {
    @Autowired
    protected DataSource dataSource;

    protected IResultProcessor<V> resultProcessor;

    /**
     * Execute a select SQL statement.
     * 
     * @author YongXia.
     * 
     * @since jdk1.6
     * @version 1.0
     * 
     * @param sql
     * @param parameters
     * @return
     * @throws SQLException
     */
    protected List<V> find(final String sql, final Serializable[] parameters) throws SQLException {
        Connection conn = null;
        PreparedStatement pst = null;
        ResultSet results = null;
        List<V> list = null;
        try {
            conn = this.dataSource.getConnection();
            pst = conn.prepareCall(sql);
            if (null != parameters)
                this.setParameters(pst, parameters);
            results = pst.executeQuery();
            list = this.resultProcessor.process(results);
        } catch (SQLException e) {
            throw e;
        } finally {
            if (null != results) {
                results.close();
                results = null;
            }
            if (null != pst) {
                pst.close();
                pst = null;
            }
            if (conn != null) {
                conn.close();
                conn = null;
            }
        }
        return list;
    }

    /**
     * Execute the save or update or delete SQL statement.
     * 
     * @author YongXia.
     * 
     * @since jdk1.6
     * @version 1.0
     * 
     * @param sql
     * @param parameters
     * @return
     * @throws SQLException
     */
    protected int saveOrUpdateOrDelete(final String sql, final Serializable[] parameters) throws SQLException {
        Connection conn = null;
        PreparedStatement pst = null;
        int updateCount = Constants.ZERO;
        try {
            conn = this.dataSource.getConnection();
            pst = conn.prepareStatement(sql);
            if (null != parameters)
                this.setParameters(pst, parameters);
            updateCount = pst.executeUpdate();

        } catch (SQLException e) {
            throw e;
        } finally {
            if (null != pst) {
                pst.close();
                pst = null;
            }
            if (null != conn) {
                conn.close();
                conn = null;
            }
        }

        return updateCount;
    }

    /**
     * Set the parameters for PreparedStatement instance.
     * 
     * @author YongXia.
     * 
     * @since jdk1.6
     * @version 1.0
     * 
     * @param pst
     * @param parameters
     * @throws SQLException
     */
    protected void setParameters(final PreparedStatement pst, final Serializable[] parameters) throws SQLException {
        if (null != parameters)
            for (int i = Constants.ZERO; i < parameters.length; ++i)
                pst.setObject(i + Constants.ONE, parameters[i]);
    }

    /**
     * The subclass must provide a IResultProcessor<V> instance.
     * 
     * @author YongXia.
     * 
     * @since jdk1.6
     * @version 1.0
     * @return
     */
    protected abstract void setResultProcessor(IResultProcessor<V> resultProcessor);
}
